#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <locale.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/extattr.h>

#include <sys/param.h>
#include <libgen.h>

#include <MediaInfoDLL_Static.h>

#define getInfo(h,k)	MediaInfo_Get((h), MediaInfo_Stream_General, 0, (k), MediaInfo_Info_Text, MediaInfo_Info_Name)
#define getInfoN(h,i,k)	MediaInfo_Get((h), MediaInfo_Stream_General, (i), (k), MediaInfo_Info_Text, MediaInfo_Info_Name)
#define getVInfo(h,i,k)	MediaInfo_Get((h), MediaInfo_Stream_Video, (i), (k), MediaInfo_Info_Text, MediaInfo_Info_Name)

struct exattr {
	char title[256];
	char duration[80];
	char resolution[80];
	char date[80];

	char filesize[80];
	char bitrate[128];
} finfo;

extern char *optarg;
extern int optind;
extern int optopt;
extern int opterr;
extern int optreset;

struct tm
parse_datetime(const char* timestr)
{
	// UTC 2011-07-15 18:59:31

	struct tm tinfo;
	sscanf(timestr + 4, "%04d-%02d-%02d %02d:%02d:%02d",
	    &tinfo.tm_year,
	    &tinfo.tm_mon,
	    &tinfo.tm_mday,
	    &tinfo.tm_hour,
	    &tinfo.tm_min,
	    &tinfo.tm_sec);
	tinfo.tm_year -= 1900;
	tinfo.tm_mon -= 1;

	tinfo.tm_zone = NULL;

	return tinfo;
};

static void
usage(char* arg0)
{
	fprintf(stderr,
	    "Usage: %s [-nv] [-t TITLE] FILENAME|DIRNAME\n"
	    "       %s -l FILENAME|DIRNAME\n"
	    "       %s -D FILENAME|DIRNAME\n",
	    arg0,
	    arg0,
	    arg0);

	exit(1);
}

static int
get_mediainfo(struct exattr* finfo, char* fname, off_t fsiz)
{
	void* h = MediaInfo_New();
	size_t s = MediaInfo_Open(h, fname);
	if (! s) {
		return -1;
	}

	if (strcmp(getInfo(h, "Format"), "MPEG-TS")) {
		fprintf(stderr, "no MPEG-TS\n");
		return -1;
	}

	struct tm start = parse_datetime(getInfo(h, "Duration_Start"));
	char buf[1024];
	strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", &start);
	strcpy(finfo->date, buf);

	int duration_ms;
	sscanf(getInfo(h, "Duration"), "%d.%*", &duration_ms);
	int duration = duration_ms / 1000;

	sprintf(finfo->bitrate, "%d", (int)((double) fsiz / duration));

	int duration_h = duration / 3600;
	duration -= (duration_h * 3600);
	int duration_m = duration / 60;
	duration -= (duration_m * 60);
	sprintf(finfo->duration, "%d:%02d:%02d.%03d",
	    duration_h,
	    duration_m,
	    duration,
	    duration_ms % 1000);

	sprintf(finfo->filesize, "%zd", fsiz);

	int width = 0;
	int height = 0;
	int n = atoi(getInfo(h, "VideoCount"));
	for (int i = 0; i < n; ++i) {
		int wi = atoi(getVInfo(h, i, "Width"));
		int he = atoi(getVInfo(h, i, "Height"));

		if (width < wi)
			width = wi;
		if (height < he)
			height = he;
	}
	sprintf(finfo->resolution, "%dx%d", width, height);

	MediaInfo_Close(h);
	MediaInfo_Delete(h);

	return 0;
}

static void
set_extattr(char* fname, struct exattr* finfo)
{
#define SET_ATTR(k,v)	extattr_set_file(fname, EXTATTR_NAMESPACE_USER, (k), (v), strlen(v))

	SET_ATTR("dc:title",   finfo->title);
	SET_ATTR("duration",   finfo->duration);
	SET_ATTR("resolution", finfo->resolution);
	SET_ATTR("dc:date",    finfo->date);
	SET_ATTR("bitrate",    finfo->bitrate);
}

static void
get_extattr(char* fname, struct exattr* finfo)
{
#define GET_ATTR(k,v)	extattr_get_file(fname, EXTATTR_NAMESPACE_USER, (k), (v), sizeof(v))

	memset(finfo, 0, sizeof(struct exattr));

	GET_ATTR("dc:title",   finfo->title);
	GET_ATTR("duration",   finfo->duration);
	GET_ATTR("resolution", finfo->resolution);
	GET_ATTR("dc:date",    finfo->date);
	GET_ATTR("bitrate",    finfo->bitrate);
}

static void
delete_extattr(char* fname)
{
#define DEL_ATTR(k)	extattr_delete_file(fname, EXTATTR_NAMESPACE_USER, (k));

	DEL_ATTR("dc:title");
	DEL_ATTR("duration");
	DEL_ATTR("resolution");
	DEL_ATTR("dc:date");
	DEL_ATTR("bitrate");
}

static void
print_finfo(char* fname, struct exattr* finfo)
{
	printf(
		"%s:\n"
		"title      %s\n"
		"duration   %s\n"
		"resolution %s\n"
		"date       %s\n"
		"bitrate    %s\n",
		fname,
		finfo->title,
		finfo->duration,
		finfo->resolution,
		finfo->date,
		finfo->bitrate);

}

int
main(int argc, char* argv[])
{
	char* arg0 = argv[0];

	//Localisation
	setlocale(LC_ALL, """""");

	if (argc < 1) {
		usage(arg0);
	}

	// ----------------------------------------

	memset(&finfo, 0, sizeof(finfo));

	int delete = 0;
	int list = 0;
	int dryrun = 0;
	int verbose = 0;

	int ch;
	while ((ch = getopt(argc, argv, "Dlnvt:")) != -1) {
		switch (ch) {
		case 'D':
			delete = 1;
			break;
		case 'l':
			list = 1;
			break;
		case 'n':
			dryrun = 1;
			break;
		case 'v':
			verbose = 1;
			break;
		case 't':
			strncpy(finfo.title, optarg, sizeof(finfo.title));
			finfo.title[sizeof(finfo.title) - 1] = '\0';
			break;
		default:
			fprintf(stderr, "Unknown option: %c\n", ch);
			usage(arg0);
		}
	}
	argc -= optind;
	argv += optind;

	// ----------------------------------------

	if (argc < 1) {
		fprintf(stderr, "no target file.\n", ch);
		usage(arg0);
	}

	while (argc > 0) {
		struct stat fstat;
		if (stat(argv[0], &fstat) < 0) {
			perror(argv[0]);
		}
		else if (list) {
			get_extattr(argv[0], &finfo);
			print_finfo(argv[0], &finfo);
		} else if (delete && !dryrun) {
			delete_extattr(argv[0]);
		} else {
			if (finfo.title[0] == 0) {
				char fn[MAXPATHLEN];
				basename_r(argv[0], fn);
				strcpy(finfo.title, fn);
			}

			if (S_ISDIR(fstat.st_mode)) {
				if (verbose)
					print_finfo(argv[0], &finfo);
				if (! dryrun)
					set_extattr(argv[0], &finfo);
			} else if (S_ISREG(fstat.st_mode)) {
				if (get_mediainfo(&finfo, argv[0], fstat.st_size) < 0) {
					perror(argv[0]);
				} else {
					if (verbose)
						print_finfo(argv[0], &finfo);
					if (! dryrun)
						set_extattr(argv[0], &finfo);
				}
			} else {
				fprintf(stderr, "%s: no file or directory.\n", argv[0]);
			}
		}

		--argc;
		++argv;
	}

	return 0;
}
